#include "Knight.h"


const int Knight::MOVE_VER[Knight::MAX_MOVE] = {-2, -2, -1,  1,  2,  2,  1, -1};
const int Knight::MOVE_HOR[Knight::MAX_MOVE] = {-1,  1,  2,  2,  1, -1, -2, -2};

Knight::Knight(LPDIRECT3DDEVICE9 d3ddv)
{
	ResetAll();
	m_fSpeedRotation = 1.0f;
	m_fSpeedMovement = 0.3f;

	m_pD3Ddv = d3ddv;
	m_pMesh = new CMesh();
	m_pMesh->LoadMeshFromFile("ChessKnight.x", m_pD3Ddv);
}

void Knight::ResetAll()
{
	m_iCurX = m_iNextX = 0;
	m_iCurY = m_iNextY = 0;
	m_iNextPosX = m_iCurPosX = 0;
	m_iNextPosY = m_iCurPosY = 0;
	m_fNextAngle = m_fCurAngle = 0.0f;
	m_iStep			= 0;
	m_iState		= 0;
	m_iCurDir		= 0;
}

void Knight::RenderKnight()
{
	m_pD3Ddv->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW);
	D3DXMatrixIdentity(&m_mtxWorld);

	D3DXMatrixRotationY(&m_mtxRotating, D3DXToRadian(m_fCurAngle));
	D3DXMatrixMultiply(&m_mtxWorld, &m_mtxWorld, &m_mtxRotating);

	D3DXMatrixScaling(&m_mtxScaling, 400.0f, 400.0f, 400.0f);
	D3DXMatrixMultiply(&m_mtxWorld, &m_mtxWorld, &m_mtxScaling);

	D3DXMatrixTranslation(&m_mtxTranslating, m_iCurPosX, 0, -m_iCurPosY - 20);
	D3DXMatrixMultiply(&m_mtxWorld, &m_mtxWorld, &m_mtxTranslating);

	m_pD3Ddv->SetTransform(D3DTS_WORLD, &m_mtxWorld);
	m_pMesh->DrawMesh(m_pD3Ddv);
}


void Knight::PlayScript(int **board)
{
	if (m_iStep % 2 != 0)
	{
		board[m_pArrMovement[m_iStep + 1][1]][m_pArrMovement[m_iStep + 1][0]] = 1;
	}
	

	switch (m_iState)
	{
	case STATE_ROTATION:
		UpdateRotation();
		break;
	case STATE_MOVE:
		UpdateMove();
		break;
	case STATE_SET_MOVE:
		UpdateSetMove();
		break;
	default:
		break;
	}
}


void Knight::UpdateRotation()
{
	if (m_fCurAngle == m_fNextAngle)
	{
		m_iState = STATE_MOVE;
	}
	else 
	{
		if (m_fCurAngle > m_fNextAngle)
		{
			if (m_fCurAngle - m_fNextAngle > m_fSpeedRotation)
				m_fCurAngle -= m_fSpeedRotation;
			else
				m_fCurAngle = m_fNextAngle;
		}
		else
		{
			if (m_fNextAngle - m_fCurAngle > m_fSpeedRotation)
				m_fCurAngle += m_fSpeedRotation;
			else
				m_fCurAngle = m_fNextAngle;
		}
	}
}


void Knight::UpdateMove()
{
	if (m_iNextPosX == 0 && m_iNextPosY == 0)
	{
		m_iState = STATE_SET_MOVE;
	}
	else
	{
		if (m_iNextPosX > 0)
		{
			if (m_iNextPosX > m_fSpeedMovement)
			{
				m_iCurPosX += m_fSpeedMovement;
				m_iNextPosX -= m_fSpeedMovement;
			}
			else
			{
				m_iCurPosX += m_iNextPosX;
				m_iNextPosX = 0;
			}
		}
		else if (m_iNextPosX < 0)
		{
			if (m_iNextPosX < m_fSpeedMovement)
			{
				m_iCurPosX -= m_fSpeedMovement;
				m_iNextPosX += m_fSpeedMovement;
			}
			else
			{
				m_iCurPosX -= m_iNextPosX;
				m_iNextPosX = 0;
			}
		}

		if (m_iNextPosY > 0)
		{
			if (m_iNextPosY > m_fSpeedMovement)
			{
				m_iCurPosY += m_fSpeedMovement;
				m_iNextPosY -= m_fSpeedMovement;
			}
			else
			{
				m_iCurPosY += m_iNextPosY;
				m_iNextPosY = 0;
			}
		}
		else if (m_iNextPosY < 0)
		{
			if (m_iNextPosY < m_fSpeedMovement)
			{
				m_iCurPosY -= m_fSpeedMovement;
				m_iNextPosY += m_fSpeedMovement;
			}
			else
			{
				m_iCurPosY -= m_iNextPosY;
				m_iNextPosY = 0;
			}
		}
	}
}

void Knight::UpdateSetMove()
{
	if (m_iStep >= m_iMaxStep)
	{
		m_iState = 0;
		MessageBox(0, "Done!", "Knight's Tour", 0);
	}
	else
	{
		m_iStep++;
		SetNextCell(m_iCurX, m_iCurY);
	}
}


void Knight::SetArrMovement(int **Arr, int w, int h)
{
	int maxStep = w * h;

	m_pArrMovement = new int* [maxStep * 2 - 1];
	for (int i = 0, size = maxStep * 2 - 1; i < size; i++)
	{
		m_pArrMovement[i] = new int[2];
	}


	for (int i = 0; i < maxStep - 1; i++)
	{
		m_pArrMovement[i * 2][0] = Arr[i][0];
		m_pArrMovement[i * 2][1] = Arr[i][1];

		if (Arr[i + 1][0] - Arr[i][0] == 2)
		{
			m_pArrMovement[i * 2 + 1][0] = Arr[i][0] + 1;
			m_pArrMovement[i * 2 + 1][1] = Arr[i][1];
		}
		else if (Arr[i + 1][0] - Arr[i][0] == -2)
		{
			m_pArrMovement[i * 2 + 1][0] = Arr[i][0] - 1;
			m_pArrMovement[i * 2 + 1][1] = Arr[i][1];
		}
		else if (Arr[i + 1][1] - Arr[i][1] == 2)
		{
			m_pArrMovement[i * 2 + 1][0] = Arr[i][0];
			m_pArrMovement[i * 2 + 1][1] = Arr[i][1] + 1;
		}
		else
		{
			m_pArrMovement[i * 2 + 1][0] = Arr[i][0];
			m_pArrMovement[i * 2 + 1][1] = Arr[i][1] - 1;
		}
	}
	m_pArrMovement[maxStep * 2 - 2][0] = Arr[maxStep - 1][0];
	m_pArrMovement[maxStep * 2 - 2][1] = Arr[maxStep - 1][1];


	m_iCurX = m_iNextX = m_pArrMovement[0][0];
	m_iCurY = m_iNextY = m_pArrMovement[0][1];
	m_iCurPosX = m_iCurX * 20;
	m_iCurPosY = m_iCurY * 20;
	m_iStep = 0;
	m_iMaxStep = maxStep * 2 - 2;
	m_iState = STATE_SET_MOVE;
}


int Knight::GetStateKnight(int nextX, int nextY)
{
	int deltaX = nextX - m_iCurX;
	int deltaY = nextY - m_iCurY;

	if (deltaX == -1)
	{
		if (deltaY == -1)
			return 3;
		else if (deltaY == 1)
			return 1;
		else
			return 2;
	}
	else if (deltaX == 1)
	{
		if (deltaY == -1)
			return 5;
		else if (deltaY == 1)
			return 7;
		else
			return 6;
	}
	else
	{
		if (deltaY == -1)
			return 4;
		else if (deltaY == 1)
			return 0;
	}
}


void Knight::SetNextCell(int nextX, int nextY)
{
	m_iCurX		= m_iNextX;
	m_iCurY		= m_iNextY;
	m_iCurDir	= m_iNextDir;

	m_iNextX	= m_pArrMovement[m_iStep][0];
	m_iNextY	= m_pArrMovement[m_iStep][1];
	
	m_iNextDir		= GetStateKnight(m_iNextX, m_iNextY);
	m_fNextAngle	= (m_iNextDir) * 45.0f;
	m_iState		= STATE_ROTATION;

	if (m_fCurAngle < 0)
		m_fCurAngle += 360.0f;
	if (abs(m_fNextAngle - m_fCurAngle) > 180.0f)
	{
		if (m_fNextAngle > m_fCurAngle)
			m_fNextAngle -= 360.0f;
		else
			m_fCurAngle  -= 360.0f;
	}

	switch (m_iNextDir)
	{
	case 0:
		m_iNextPosY =  + 20.0f;
		break;
	case 1:
		m_iNextPosX =  - 20.0f;
		m_iNextPosY =  + 20.0f;
		break;
	case 2:
		m_iNextPosX =  - 20.0f;
		break;
	case 3:
		m_iNextPosX =  - 20.0f;
		m_iNextPosY =  - 20.0f;
		break;
	case 4:
		m_iNextPosY =  - 20.0f;
		break;
	case 5:
		m_iNextPosX =  + 20.0f;
		m_iNextPosY =  - 20.0f;
		break;
	case 6:
		m_iNextPosX =  + 20.0f;
		break;
	case 7:
		m_iNextPosX =  + 20.0f;
		m_iNextPosY =  + 20.0f;
		break;
	}
}


void Knight::SetSpeedKnight(float speedRot, float speedMov)
{
	m_fSpeedMovement = speedMov;
	m_fSpeedRotation = speedRot;
}